﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using COM.PreAlarm.BLL;
using COM.PreAlarm.Model;

namespace UDPWinListen
{
    public partial class FrmUdpListen : Form
    {
        private object objLock = new object();
        private UdpClient recClient;
        private UdpClient sendCliet;
        //定义一个数据字典存储远端连接
        static Dictionary<string, UdpFlagEndPoint> dictUdp = new Dictionary<string, UdpFlagEndPoint>();

        public FrmUdpListen()
        {
            InitializeComponent();
            CheckForIllegalCrossThreadCalls = false;
        }

        //窗体加载事件
        private void FrmUdpListen_Load(object sender, EventArgs e)
        {
            //设置监听的ip和端口号码
            SetIpAndPort();

            //初始化 接收和发送端点
            IPEndPoint recEndPoint = new IPEndPoint(IPAddress.Parse(this.txtIp.Text.Trim()),Convert.ToInt32( this.txtRecPort.Text.Trim()));
            IPEndPoint sendEndPoint = new IPEndPoint(IPAddress.Parse(this.txtIp.Text.Trim()), Convert.ToInt32(this.txtsendport.Text.Trim()));
            //初始化接收和发送socket
            recClient = new UdpClient(recEndPoint);
            sendCliet = new UdpClient(sendEndPoint);

            
           
        }

        //修改ip和端口号码
        private void btnUpIp_Click(object sender, EventArgs e)
        {
            string ip = this.txtIp.Text.Trim();
            string recPort = this.txtRecPort.Text.Trim();
            string sendPort = this.txtsendport.Text.Trim();
            new Common().WriteIPAndPort(ip, recPort, sendPort);
            MessageBox.Show("修改成功");
        }

        private void btnListen_Click(object sender, EventArgs e)
        {
            BeginRecive();
        }


        //还没有做处理 udp没有必要做处理 udp好像没必要专门去停止监听（本身就没有监听）
        private void btnStopListen_Click(object sender, EventArgs e)
        {
            
        }

       

        //设置监听的ip和端口号码
        private void SetIpAndPort()
        {
            string[] strIpPort = new Common().GetIPAndPort();
            this.txtIp.Text = strIpPort[0];
            this.txtRecPort.Text = strIpPort[1];
            this.txtsendport.Text=strIpPort[2];
            
 
        }

        //开始接受客户端数据 启动线程池
        private void BeginRecive()
        {
            this.labMsg.Text = "监听服务已经启动（UDP）。。。。。";
            ThreadPool.QueueUserWorkItem(new WaitCallback(this.ReciveData));
        }

        //开始接受客户端数据
        private void ReciveData(object obj)
        {
           //准备一个远程端点，用来接收连接
            IPEndPoint remotePoint = new IPEndPoint(IPAddress.Any,0);

            while (true)
            {
                try
                {
                    //接收连接的数据
                    byte[] receiveBytes = recClient.Receive(ref remotePoint);
                    string receiveMessage = Encoding.GetEncoding("gb2312").GetString(receiveBytes, 0, receiveBytes.Length);
                    char strHeader; //记录信息头字符
                    string phone;//记录设备的电话号码
                    string verNumber;//记录设备的版本号
                    string retStatus;//记录协议返回的状态码 
                    
                    if (receiveMessage.Length > 0)
                    {
                        strHeader = receiveMessage[0];//获取信息的头字符
                        phone = receiveMessage.Substring(2, 11);//获取信息的电话号码
                        
                        //获取终端连接心跳包
                        if (strHeader == 'R')
                        {
                            //接收到一个连接心跳后立即给其回复信息，表示已经收到你心跳包。
                            byte[] backArr = BackRMsg(phone);
                            recClient.Send(backArr,backArr.Length,remotePoint);

                            verNumber = receiveMessage.Substring(20);//获取接收机的版本号码
                            if (dictUdp.ContainsKey(phone))
                            {
                                //非第一次接到心跳包 有可能是断开后重新发起的连接，也有可能是终端连续发送的
                               
                                    lock (objLock)
                                    {
                                        dictUdp[phone].IsOnline = 1; 
                                    }

                                dictUdp[phone].RemoteEnd = remotePoint;
                                dictUdp[phone].VerNumber=verNumber;
                                if (dictUdp[phone].Equip_No != null)
                                {
                                    // 更新状态 标示位在线
                                    UpdateEquipOnlineWhenRecR(dictUdp[phone].Equip_No);
                                }
                            }
                            else
                            {
                                //第一次终端连接 加入一个对象终端并初始化必要的数据
                                UdpFlagEndPoint uep = new UdpFlagEndPoint();
                                uep.Telephone = phone;
                                //这个在线状态 有可能会被多个线程同时改动所有要上锁
                                lock (objLock)
                                {
                                    uep.IsOnline = 1;
                                }
                                uep.VerNumber = verNumber;
                                uep.RemoteEnd = remotePoint;
                                uep.Equip_No = GetEquipNoFromEquipInstalledTable(phone);
                                dictUdp.Add(phone, uep);
                                if (uep.Equip_No != null)
                                {
                                    //更新设备在线状态 标示为在线。
                                    UpdateEquipOnlineWhenRecR(uep.Equip_No);
                                }
                                else
                                {
                                    //如果对应手机好号码的接收机编号为空的话，有可能是非正常连接
                                    dictUdp.Remove(phone);
                                }

                            }
                        }
                        else if (strHeader == 'D')
                        {
                            //发送数据后收到的反馈数据 以确定终端已经收到了服务器发送的数据
                            //与tcp大不相同，只有在收到接收机的反馈才能确定是否发送成功
                            retStatus = receiveMessage.Substring(15,4);//记录返回的状态 DATA
                            if (retStatus == "DATA") { 
                                //返回DATA时说明 接收机已经接收到信息
                                string equipNO = dictUdp[phone].Equip_No;
                                string msgNo = dictUdp[phone].Msg_No;
                                UpdateEquipMsg_Sent(equipNO, msgNo);
                            }
                            
                        }
                        else if (strHeader == 'S')
                        {
                            //设置命令 成功后返回的数据
                            dictUdp[phone].Status = receiveMessage.Substring(19, 2);//设置成功返回OK
                            
                            //if (retStatus == "OK") 
                            string equipNO = dictUdp[phone].Equip_No;
                            string msgNo = dictUdp[phone].Msg_No;
                            UpdateSetMsgSent(dictUdp[phone]);
                            
                        }
                        else if (strHeader == 'M')
                        {
                            //处理手机短信 因为手机短信和GPRS方式不同，短信是先发送出去后才记录数据的
                            //所以对于短信，当回执M时短信已经播出，再返回来记录短信内容
                            string equipNo=dictUdp[phone].Equip_No; //= GetEquipNoFromEquipInstalledTable(phone);//有电话号码获取设备编码
                            if (equipNo == null)
                            {
                                equipNo = GetEquipNoFromEquipInstalledTable(phone);
                            }
                            //string sendPhone = receiveMessage.Substring(16, 11);//发送信息者的电话号码
                            //string sendTime = receiveMessage.Substring(36, 19);// 信息的发送时间
                            string sendMsg ="短信来自："+receiveMessage.Substring(16, 11)+"  "+ receiveMessage.Substring(36);//信息内容 发信息的电话号码 时间和内容
                            RecTelMsgOrTelphone(sendMsg,equipNo);

                        }
                        else if (strHeader == 'T') {
                            //广播返回信息
                            string equipNo = dictUdp[phone].Equip_No;
                            if (equipNo == null)
                            {
                                equipNo = GetEquipNoFromEquipInstalledTable(phone);
                            }
                            string sendMsg = "电话来自："+receiveMessage.Substring(16, 11) + "  " + receiveMessage.Substring(36) ;
                            RecTelMsgOrTelphone(sendMsg, equipNo);
                        }
                        else if (strHeader == 'U')
                        {
                            //远程升级命令回执信息
                            dictUdp[phone].Status = receiveMessage.Substring(29).Trim();//下位机接收到升级命令后的返回信息
                            //int len = retStatus.Length;
                            //if (retStatus == "OK") 
                            string equipNO = dictUdp[phone].Equip_No;
                            string msgNo = dictUdp[phone].Msg_No;
                            UpdateSetMsgSent(dictUdp[phone]);
                            
                        }
                    }
                }
                catch(Exception e)
                {

                }
                
                Thread.Sleep(300);//休眠300毫秒 以减轻cpu压力
            }
        }

        Equip_MsgSent ems = new Equip_MsgSent();

        //更新信息发布备案表，将信息的发送状态置为1（已接收）
        private void UpdateEquipMsg_Sent(string equipNO, string msgNo)
        {
           
            try
            {
                ems.Update("Msg_RecStatus", " Equip_No='" + equipNO + "' and Msg_No='" + msgNo + "'", "1", null);
            }
            catch (Exception ex)
            {
                //防止网络连接异常不做处理
            }
            //throw new NotImplementedException();
        }

        Equip_SetMsg esm = new Equip_SetMsg();
        //更新设置信息表
        private void UpdateSetMsgSent(UdpFlagEndPoint ufep)
        {
            if (ufep.Status == "OK")
            {
                esm.Update("Ext", "Equip_No='" + ufep.Equip_No + "' and SetMsg_No='" + ufep.Msg_No + "'", "设置成功", null);
            }
            else if (ufep.Status == "BEGIN")
            {
                esm.Update("Ext", "Equip_No='" + ufep.Equip_No + "' and SetMsg_No='" + ufep.Msg_No + "'", "升级中...", null);
            }
            else if (ufep.Status == "STOP")
            {
                esm.Update("Ext", "Equip_No='" + ufep.Equip_No + "' and SetMsg_No='" + ufep.Msg_No + "'", "停止升级", null);
            }
            else if (ufep.Status == "D_OK")
            {
                //数据下载完毕 这里可以不做处理
 
            }
            else if (ufep.Status == "ERROR")
            {
                esm.Update("Ext", "Equip_No='" + ufep.Equip_No + "' and SetMsg_No='" + ufep.Msg_No + "'", "升级失败", null);
            }
            else if (ufep.Status == "A_OK")
            {
                esm.Update("Ext", "Equip_No='" + ufep.Equip_No + "' and SetMsg_No='" + ufep.Msg_No + "'", "升级成功", null);
                //远程升级成功之后要更新设备的版本号
                UpdateVerNumber(ufep);
            }
            else
            {
                esm.Update("Ext", "Equip_No='" + ufep.Equip_No + "' and SetMsg_No='" + ufep.Msg_No + "'", "设置失败", null);

            }
            
        }

        #region 信息发送
        //定时器 每3秒钟扫描一次信息发送备案表，看看是否有未发送的信息，如有则发送
        private void timSendMsg_Tick(object sender, EventArgs e)
        {
            SendMessages();
        }

        //得到数据表 包含设备电话号码，设备编号，信息编号，信息内容。
        //从信息备案表中取出最近十分钟还没有发出的数据
        private DataTable GetEquipInfoMsgSendInfo()
        {
            DataTable dt = new DataTable();
            try
            {
                Equip_MsgSent _Equip_MsgSent = new Equip_MsgSent();
                List<DBParameter> _listDBParameter = new List<DBParameter>();
                _listDBParameter.Add(new DBParameter("@TranType", "WaitMsgList"));

                IDataReader sdr = _Equip_MsgSent.ExecuteReader("[dbo].[Msg_Sel]", _listDBParameter, System.Data.CommandType.StoredProcedure);
                dt.Load(sdr);

            }
            catch (Exception ex)
            {//发生异常（防止网络连接造成的异常，不做处理）

            }
            return dt;
        }

        //发送信息
        private void SendMessages()
        {

            DataTable dt = GetEquipInfoMsgSendInfo();
            if (dt.Rows.Count > 0)
            {

                foreach (DataRow dr in dt.Rows)
                {
                    string str = Convert.ToString(dr["Equip_SimNo"]);
                    if (dictUdp.ContainsKey(str))
                    {
                        //首先进行信息的发送
                        string header = "D 12345678+##:";
                        string strMsg = header + Convert.ToString(dr["Msg_Content"])+":##+";//信息发送要开始加上发送次数
                        byte[] arrMsg = System.Text.Encoding.GetEncoding("gb2312").GetBytes(strMsg);
                        //upd 的发送不同于tcp 这里直接用接收端进行数据发送
                        recClient.Send(arrMsg, arrMsg.Length, dictUdp[str].RemoteEnd);
                        
                        //更新标识类的值 设备编码和信息编号 为更新发送状态做准备。
                        
                        dictUdp[str].Equip_No=dr["Equip_No"].ToString();
                        dictUdp[str].Msg_No=dr["Msg_No"].ToString();

                    }
                }

            }
            this.labMsg.Text = dt.Rows.Count.ToString();
            //throw new NotImplementedException();
        } 
        #endregion

        #region 状态更新
        //定时器 2分钟 其扫描一次数据字典 更新设备状态表 将在线的的置为1 ，不在线的置为0
        private void timUpdateEquipStatus_Tick(object sender, EventArgs e)
        {
            UpdateEquipOnlieStatus();
        }
        Equip_Online eo = new Equip_Online();
        //更新设备的在线状态
        Common com = new Common();

        //在服务器接收到接受机的心跳包的时候就更新在线状态使之状态为在线
        private void UpdateEquipOnlineWhenRecR(string equipNo)
        {
            string nowTime = com.GetDateString();
            List<DBParameter> _listDBParameter1 = new List<DBParameter>();
            _listDBParameter1.Add(new DBParameter("@Equip_No", equipNo));
            _listDBParameter1.Add(new DBParameter("@Rpt_DateTime", nowTime));
            _listDBParameter1.Add(new DBParameter("@flage", "online"));
            eo.ExecuteNonQuery("[dbo].[AddOrUpdateEquipOnline]", _listDBParameter1, System.Data.CommandType.StoredProcedure);
            
 
        }

        //在定时器中 更新设备的在线状态，是不在线的得到标示。
        private void UpdateEquipOnlieStatus()
        {
            string nowTime = com.GetDateString();

            if (dictUdp.Count > 0)
            {
                
                List<string> listudp = new List<string>(dictUdp.Keys);
                for (int i = 0; i < listudp.Count; i++)
                {
                    if (dictUdp[listudp[i]].Equip_No == null)
                    {
                        //终端刚连到服务器，还没有通过信息发送来初始化 设备编码的值，此时设备编码为空
                        string equipNo = GetEquipNoFromEquipInstalledTable(listudp[i]);
                        if (equipNo != null)
                        {
                            dictUdp[listudp[i]].Equip_No = equipNo;
                        }                      
                    }
                    if (dictUdp[listudp[i]].Equip_No != null)
                    {
                        //if (dictUdp[listudp[i]].IsOnline > 0)
                        //{

                        //    //说明设备在线
                        //    List<DBParameter> _listDBParameter1 = new List<DBParameter>();
                        //    _listDBParameter1.Add(new DBParameter("@Equip_No", dictUdp[listudp[i]].Equip_No));
                        //    _listDBParameter1.Add(new DBParameter("@Rpt_DateTime", nowTime));
                        //    _listDBParameter1.Add(new DBParameter("@flage", "online"));
                        //    eo.ExecuteNonQuery("[dbo].[AddOrUpdateEquipOnline]", _listDBParameter1, System.Data.CommandType.StoredProcedure);
                        //    //更新完设备在线状态后将终端类中的在线状态属性值标志设为0
                        //    //此时改动这个值得时候有可能在接收心跳的时候也会改动这个值，所以要加锁来保证数据的统一性
                        //    lock (objLock)
                        //    {
                        //        dictUdp[listudp[i]].IsOnline -= 1;
                        //    }

                        //}
                        dictUdp[listudp[i]].IsOnline--;
                        if (dictUdp[listudp[i]].IsOnline <0)
                        {
                            //设备不在线
                            List<DBParameter> _listDBParameter1 = new List<DBParameter>();
                            _listDBParameter1.Add(new DBParameter("@Equip_No", dictUdp[listudp[i]].Equip_No));
                            _listDBParameter1.Add(new DBParameter("@Rpt_DateTime", nowTime));
                            _listDBParameter1.Add(new DBParameter("@flage", "offline"));
                            eo.ExecuteNonQuery("[dbo].[AddOrUpdateEquipOnline]", _listDBParameter1, System.Data.CommandType.StoredProcedure);
                            dictUdp[listudp[i]].IsOnline = 0;//将标示位归0
                        } 
                    }
                    
                }
            }
        }

        Equip_Installed ei = new Equip_Installed();
        /// <summary>
        /// 通过电话号码获取对应的设备编码 
        /// 使用条件，当一个设备刚连到服务器的时候，还没有经过信息发送来初始化设备编码，此时设备编码为空，要初始化一下
        /// 
        /// </summary>
        /// <param name="phone"></param>
        private string  GetEquipNoFromEquipInstalledTable(string phone)
        {
            string equipNo;
            phone = phone.Trim();
           
            Equip_InstalledInfo eiIfo = ei.GetModel("Equip_No", " Equip_SimNo='" + phone + "'", "", ei.TableName, null);
            if (eiIfo != null)
            {
                equipNo = eiIfo.Equip_No;
            }
            else
            {
                equipNo = null;
            }
            return equipNo;

        }

        //更新设备的版本号码
        private void UpdateVerNumber(UdpFlagEndPoint ufep)
        {
            ei.Update("Equip_Version", " Equip_No='" + ufep.Equip_No + "'", ei.TableName,ufep.VerNumber, null);
        }
        
        #endregion


        /// <summary>
        /// 记录时候短信或者是电话通话的相关内容
        /// </summary>
        /// <param name="tMsg">短信内容或者是电话相关信息</param>
        /// <param name="equipNo">接收机编码</param>
        public void RecTelMsgOrTelphone(string tMsg,string equipNo)
        {

            //string tMsg = context.Request["sendMsg"];

            //string equipNo = context.Request["equipNo"];
            //生成信息编码
            string msgNo = equipNo.Substring(0, 4) + "FS_" + string.Format("{0}", DateTime.Now.ToString("yyyyMMddHHmmss"));
            COM.PreAlarm.Model.Msg_SentInfo msi = new COM.PreAlarm.Model.Msg_SentInfo();
            msi.Msg_No = msgNo;
            msi.Msg_Content = tMsg;
            COM.PreAlarm.BLL.Msg_Sent msg = new COM.PreAlarm.BLL.Msg_Sent();
            msg.Add(msi, "*");//插入信息表

            COM.PreAlarm.BLL.Equip_MsgSent ems = new COM.PreAlarm.BLL.Equip_MsgSent();
            COM.PreAlarm.Model.Equip_MsgSentInfo emsi = new COM.PreAlarm.Model.Equip_MsgSentInfo();
            emsi.SetIsNew(true);
            emsi.Equip_No = equipNo;
            emsi.Msg_No = msi.Msg_No;
            emsi.Msg_SendTime = DateTime.Now;
            emsi.Msg_RecStatus = 1;
            ems.Add(emsi, "*");//插入信息备案表

        }

       
        /// <summary>
        /// 发送设置信息
        /// </summary>
        private void SendSetMsg()
        {
            DataTable dt = Common.GetSetMsgData();
            if (dt.Rows.Count > 0)
            {
                foreach (DataRow dr in dt.Rows)
                {
                    string simPhone = Convert.ToString(dr["Equip_SimNo"]);
                    if (dictUdp.ContainsKey(simPhone))
                    {
                        string strMsg = Convert.ToString(dr["SetMsg_Content"]);
                        byte[] arrMsg = System.Text.Encoding.GetEncoding("gb2312").GetBytes(strMsg);
                        //upd 的发送不同于tcp 这里直接用接收端进行数据发送
                        recClient.Send(arrMsg, arrMsg.Length, dictUdp[simPhone].RemoteEnd);

                        dictUdp[simPhone].Equip_No = Convert.ToString(dr["Equip_No"]);
                        dictUdp[simPhone].Msg_No = Convert.ToString(dr["SetMsg_No"]);
                        esm.Update("SetMsg_RecStatus", "Equip_No='" + dictUdp[simPhone].Equip_No + "' and SetMsg_No='" + dictUdp[simPhone].Msg_No + "'", "1", null);
                    }
                }
            }
 
        }

        private void timSendSetMsg_Tick(object sender, EventArgs e)
        {
            SendSetMsg();
            this.labMsg.Text = "发送设置信息";
        }

        //返回一个带电话号码的byte数组，用来向接收机返回数据，表示接收到其发送给服务器的心跳包。
        private  byte[] BackRMsg(string phone)
        {
            string strBack = "12345678R " + phone + "F";
            byte[] byteBack = System.Text.Encoding.GetEncoding("gb2312").GetBytes(strBack);
            return byteBack;
 
        }

    }
}
